package com.wn.netty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;

/**
 * @FileName: SelectorTest
 * @Description: NIO-server (存在BUG，会导致死循环，CPU100%. 解决方案：判断发生了JVMBUG后重新构建Selector)
 * @Author: MaWenHao
 * @Created: 2020/7/24 - 15:57
 * @Version: V1.0
 * @Company: 东方微银科技（北京）有限公司
 */
public class NIOServerTest {

    private static final LongAdder JVM_BUG_COUNT = new LongAdder();

    public static void main(String[] args) throws IOException {
        // 创建选择器
        Selector selector = Selector.open();
        // 创建serverSocket并绑定端口
        final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        // 设置非阻塞模式
        serverSocketChannel.configureBlocking(false);
        // 监听serverSocket的accept时间
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 记录开始时间
        long startTime = System.nanoTime();
        // 超时时间
        long timeout = 3000L;
        // 循环处理监听
        while (true) {
            int count = selector.select(timeout);
            long endTime = System.nanoTime();
            if (endTime - startTime < TimeUnit.MILLISECONDS.toNanos(timeout)) {
                JVM_BUG_COUNT.increment();
                System.out.println("JVM_BUG次数:" + JVM_BUG_COUNT.intValue());
                if (JVM_BUG_COUNT.intValue() >= 12) {
                    System.out.println("开始重构Selector......");
                    // 如果超过阈值，重构selector
                    final Selector newSelector = Selector.open();
                    for (SelectionKey key: selector.keys()) {
                        Object a = key.attachment();
                        try {
                            if (!key.isValid() || key.interestOps() == 0) {
                                continue;
                            }

                            int interestOps = key.interestOps();
                            key.cancel();
                            key.channel().register(newSelector, interestOps, a);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    // 关闭旧的selector
                    selector.close();
                    // 将新的赋值给原来的selector
                    selector = newSelector;
                    // 重置JVM_BUG数
                    JVM_BUG_COUNT.reset();
                    System.out.println("Selector重构完成......");
                }
            }
            startTime = endTime;
            if (count == 0) {
                System.out.println("服务器没有监听到ACCEPT事件......Selector: " + selector);
                continue;
            }
            // Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // selectionKeys.removeIf(selectionKey -> {
            //     if (selectionKey.isAcceptable()) {
            //         try {
            //             SocketChannel socketChannel = serverSocketChannel.accept();
            //             socketChannel.configureBlocking(false);
            //             socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
            //             System.out.println("READ时间注册成功:" + socketChannel);
            //             return true;
            //         } catch (IOException e) {
            //             e.printStackTrace();
            //         }
            //     }
            //     if (selectionKey.isReadable()) {
            //         ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
            //         try (SocketChannel channel = (SocketChannel) selectionKey.channel();) {
            //             channel.read(buffer);
            //             System.out.println(new String(buffer.array()));
            //             return true;
            //         } catch (IOException e) {
            //             e.printStackTrace();
            //         }
            //     }
            //     return false;
            // });
        }
    }

}
